#if _MSC_VER
#else // #if _MSC_VER
#  include <stdio.h>
#  include <fcntl.h>
#  include <sys/stat.h>
#endif // #if _MSC_VER
#include "hfile.h"
#include "hstring.h"

hfile_t HPLATFORMCALL hfile_open(const char* file_name, int file_access, int flags)
{
#if _MSC_VER
    if (!file_name)
    {
        return HF_INVALID_FILE;
    }
    else
    {
        wchar_t* file_name16 = hstr_g2w(file_name);
        hfile_t f = hfile_open16(file_name16, file_access, flags);
        HSTR_FREE(file_name16);
        return f;
    }
#else // #if _MSC_VER
    int flag = 0;

    if (!file_name)
    {
        return HF_INVALID_FILE;
    }

    if ((HOA_READ & file_access) && (HOA_WRITE & file_access))
    {
        flag = O_RDWR;
    }
    else if (HOA_READ & file_access)
    {
        flag = O_RDONLY;
    }
    else if (HOA_WRITE & file_access)
    {
        flag = O_WRONLY;
    }
    else
    {
        return HF_INVALID_FILE;
    }

    if (HOF_CREATE == flags)
    {
        flag |= O_CREAT;
    }
    else if (HOF_NEW == flags)
    {
        flag |= (O_CREAT | O_EXCL);
    }
    else if (HOF_OPEN == flags)
    {
    }
    else if (HOF_TRUNCATE == flags)
    {
        flag |= O_TRUNC;
    }
    else
    {
        return HF_INVALID_FILE;
    }

    return (hfile_t)open(file_name, flag, 0666);
#endif // #if _MSC_VER
}

#if _MSC_VER
hfile_t HPLATFORMCALL hfile_open16(const wchar_t* file_name, int file_access, int flags)
{
    HANDLE hFile = INVALID_HANDLE_VALUE;
    DWORD dwAccess = 0;
    DWORD dwFlags = HOF_CREATE == flags ? CREATE_ALWAYS :
        HOF_NEW == flags ? CREATE_NEW :
        HOF_OPEN == flags ? OPEN_EXISTING :
        HOF_TRUNCATE == flags ? TRUNCATE_EXISTING : 0;

    if (0 == dwFlags || !file_name)
    {
        return HF_INVALID_FILE;
    }
    if (file_access & HOA_READ)
    {
        dwAccess |= GENERIC_READ;
    }
    if (file_access & HOA_WRITE)
    {
        dwAccess |= GENERIC_WRITE;
    }

    hFile = CreateFileW(file_name, dwAccess, FILE_SHARE_READ, NULL, dwFlags, FILE_ATTRIBUTE_NORMAL, NULL);
    if (INVALID_HANDLE_VALUE == hFile)
    {
        return HF_INVALID_FILE;
    }

    return (hfile_t)hFile;
}
#endif

int HPLATFORMCALL hfile_close(hfile_t file)
{
#if _MSC_VER
    if (CloseHandle((HANDLE)file))
    {
        return 1;
    }

    return 0;
#else // #if _MSC_VER
    return close((int)file);
#endif // #if _MSC_VER
}

int HPLATFORMCALL hfile_read(hfile_t file, char* buff, int buf_size)
{
#if _MSC_VER
    DWORD dwReaded = 0;
    if (!ReadFile((HANDLE)file, (LPVOID)buff, (DWORD)buf_size, &dwReaded, NULL))
    {
        return -1;
    }

    return (int)dwReaded;
#else // #if _MSC_VER
    return (int)read((int)file, (void*)buff, (size_t)buf_size);
#endif // #if _MSC_VER
}

int HPLATFORMCALL hfile_write(hfile_t file, const char* buff, int buf_size)
{
#if _MSC_VER
    DWORD dwWrited = 0;
    if (!WriteFile((HANDLE)file, (LPVOID)buff, (DWORD)buf_size, &dwWrited, NULL))
    {
        return -1;
    }

    return (int)dwWrited;
#else // #if _MSC_VER
    return (int)write((int)file, (void*)buff, (size_t)buf_size);
#endif // #if _MSC_VER
}

hfile_off_t HPLATFORMCALL hfile_getsize(hfile_t file)
{
#if _MSC_VER
    LARGE_INTEGER li = {0};
    if (!GetFileSizeEx((HANDLE)file, &li))
    {
        return HF_OPERATION_FAILED;
    }

    return (hfile_off_t)li.QuadPart;
#else // #if _MSC_VER
    struct stat buf = {0};
    if (0 != fstat((int)file, &buf))
    {
        return HF_OPERATION_FAILED;
    }

    return (hfile_off_t)buf.st_size;
#endif // #if _MSC_VER
}

int HPLATFORMCALL hfile_truncate(hfile_t file)
{
#if _MSC_VER
    if (SetEndOfFile((HANDLE)file))
    {
        return 0;
    }

    return 1;
#else // #if _MSC_VER
    return ftruncate((int)file, (off_t)hfile_offset(file, 0, HOM_CURRENT));
#endif // #if _MSC_VER
}

hfile_off_t HPLATFORMCALL hfile_offset(hfile_t file, hfile_off_t offset, int method)
{
#if _MSC_VER
    LARGE_INTEGER liDistance = {0};
    LARGE_INTEGER liNewOffset = {0};
    DWORD dwMethod = HOM_CURRENT == method ? FILE_CURRENT : HOM_END == method ? FILE_END : FILE_BEGIN;
    liDistance.QuadPart = offset;
    if (SetFilePointerEx((HANDLE)file, liDistance, &liNewOffset, dwMethod))
    {
        return (hfile_off_t)liNewOffset.QuadPart;
    }

    return -1;
#else // #if _MSC_VER
    int whence = HOM_CURRENT == method ? SEEK_CUR : HOM_END == method ? SEEK_END : SEEK_SET;

    return (hfile_off_t)lseek((int)file, (off_t)offset, whence);
#endif // #if _MSC_VER
}

int HPLATFORMCALL hfile_copy(const char* copy_this, const char* to_this)
{
#if _MSC_VER
    if (!copy_this || !to_this)
    {
        return 1;
    }
    else
    {
        wchar_t* copy_this16 = hstr_g2w(copy_this);
        wchar_t* to_this16 = hstr_g2w(to_this);

        int nRet = hfile_copy16(copy_this16, to_this16);

        HSTR_FREE(copy_this16);
        HSTR_FREE(to_this16);

        return nRet;
    }
#else // #if _MSC_VER
    int ret = 1;
    hfile_t file_src = HF_INVALID_FILE;
    hfile_t file_dst = HF_INVALID_FILE;

    do
    {
        char buf[4096];
        int readed = 0;

        file_src = hfile_open(copy_this, HOA_READ, HOF_OPEN);
        if (HF_INVALID_FILE == file_src)
        {
            break;
        }

        file_dst = hfile_open(to_this, HOA_WRITE, HOF_CREATE);
        if (HF_INVALID_FILE == file_dst)
        {
            break;
        }

        ret = 0;
        while (readed = hfile_read(file_src, buf, 4096))
        {
            if (HF_OPERATION_FAILED == readed)
            {
                ret = 1;
                break;
            }

            if (hfile_write(file_dst, buf, readed) != readed)
            {
                ret = 1;
                break;
            }
        }
    } while (0);

    if (HF_INVALID_FILE != file_src)
    {
        hfile_close(file_src);
    }

    if (HF_INVALID_FILE != file_dst)
    {
        hfile_close(file_dst);
    }

    return ret;
#endif // #if _MSC_VER
}

#if _MSC_VER
int HPLATFORMCALL hfile_copy16(const wchar_t* copy_this, const wchar_t* to_this)
{
    if (CopyFileW(copy_this, to_this, FALSE))
    {
        return 0;
    }

    return 1;
}
#endif // #if _MSC_VER

int HPLATFORMCALL hfile_move(const char* move_this, const char* to_this)
{
#if _MSC_VER
    if (!move_this || !to_this)
    {
        return 1;
    }
    else
    {
        wchar_t* move_this16 = hstr_g2w(move_this);
        wchar_t* to_this16 = hstr_g2w(to_this);

        int nRet = hfile_move16(move_this16, to_this16);

        HSTR_FREE(move_this16);
        HSTR_FREE(to_this16);

        return nRet;
    }
#else // #if _MSC_VER
    if (0 == rename(move_this, to_this))
    {
        return 0;
    }

    if (0 == hfile_copy(move_this, to_this) && 0 == unlink(move_this))
    {
        return 0;
    }

    // unlink(to_this);

    return 1;
#endif // #if _MSC_VER
}

#if _MSC_VER
int HPLATFORMCALL hfile_move16(const wchar_t* move_this, const wchar_t* to_this)
{
    if (MoveFileW(move_this, to_this))
    {
        return 0;
    }

    return 1;
}
#endif // #if _MSC_VER

int HPLATFORMCALL hfile_delete(const char* delete_this)
{
#if _MSC_VER
    if (DeleteFileA(delete_this))
    {
        return 0;
    }

    return 1;
#else // #if _MSC_VER
    return unlink(delete_this);
#endif // #if _MSC_VER
}

#if _MSC_VER
int HPLATFORMCALL hfile_delete16(const wchar_t* delete_this)
{
    if (DeleteFileW(delete_this))
    {
        return 0;
    }

    return 1;
}
#endif // #if _MSC_VER

int HPLATFORMCALL hfile_sync(hfile_t file)
{
#if _MSC_VER
    if (FlushFileBuffers((HANDLE)file))
    {
        return 0;
    }
    
    return 1;
#else // #if _MSC_VER
    return fsync((int)file);
#endif // #if _MSC_VER
}
